﻿namespace net.zxteam.ZXLexer.Internal
{
	using System;
	using System.Collections.Generic;
	using System.Text;

	internal interface IScope : IDisposable
	{
		void Commit();
	}

	internal interface IIndent : IDisposable
	{
	}

	internal class ParsingSourceScopeWrapper : ILexerSource
	{
		//private readonly int _wrappedInitPosition;
        private readonly ILexerSource _wrappedParsingSource;
		private readonly string _text;
		private readonly Stack<Scope> _scopeStack = new Stack<Scope>();
		private int _offset = -1;
		private int _indentLevel = 0;

		private char _prevChar;

		public event Action<int> IndentNewLine;

        public ParsingSourceScopeWrapper(ILexerSource wrappedParsingSource)
		{
			if (wrappedParsingSource == null) throw new ArgumentNullException();

			this._wrappedParsingSource = wrappedParsingSource;
			//_wrappedInitPosition = wrappedParsingSource.Position;
			StringBuilder sb = new StringBuilder();
			while (wrappedParsingSource.MoveNext())
			{
				sb.Append(wrappedParsingSource.Current);
			}
			this._text = sb.ToString();
		}

		public IScope CreateScope() { return new Scope(this); }

		public IIndent CreateIndent() { return new Indent(this); }

		public override bool MoveNext()
		{
			if (this._offset < this._text.Length)
			{
				char prevChMinus2 = _prevChar;
				char prevChMinus1 = char.MinValue;

				if (this._offset >= 0 && this._offset < _text.Length)
				{
					this._prevChar = this.Current;
					prevChMinus1 = this._prevChar;
				}

				++this._offset;

				if (this._offset < this._text.Length)
				{
					//if (prevChMinus1 == 10 && prevChMinus2 == 13)
					if (this.Current == 10 && prevChMinus1 == 13)
					{
						OnNewLine();
					}

					return true;
				}
			}

			return false;

		}

		public bool HasNext { get { return _offset + 1 < _text.Length; } }

		public override char Current
		{
			get
			{
				if (this._offset >= 0 && this._offset < this._text.Length)
				{
					return this._text[this._offset];
				}

				throw new InvalidOperationException();
			}
		}

		public override void Reset() { this._offset = -1; this._prevChar = (char)0; }

		public override int Position { get { return this._offset; } }

		public char Read()
		{
			if (this.MoveNext()) return this.Current;

			throw new Exception("" + this.StatePosition + ":" + this.Position);
		}

		public int StatePosition
		{
			get
			{
				if (_scopeStack.Count > 0) return _scopeStack.Peek()._initPosition;

				return -1;
			}
		}

		public string Tail
		{
			get
			{
				int pos = this._offset + 1;
				if (pos < _text.Length)
				{
					return _text.Substring(pos);
				}
				return string.Empty;
			}

		}

		public override string ToString() { return this.Tail; }


		private void OnNewLine()
		{
			if (_indentLevel > 0)
			{
				var handler = IndentNewLine;
				if (handler != null)
				{
					handler(_indentLevel);
				}
			}
		}

		private class Scope : IScope
		{
			private readonly ParsingSourceScopeWrapper _owner;
			public readonly int _initPosition;
			private bool _wasCommited;

			public Scope(ParsingSourceScopeWrapper owner)
			{
				this._owner = owner;

				this._initPosition = this._owner._offset;
				this._owner._scopeStack.Push(this);
			}

			public void Commit() { this._wasCommited = true; }

			public void Dispose()
			{
				var stack = this._owner._scopeStack;
				if (stack.Pop() != this) throw new InvalidOperationException();
				if (!_wasCommited) this._owner._offset = this._initPosition;
			}
		}

		private class Indent : IIndent
		{
			private readonly ParsingSourceScopeWrapper _owner;

			public Indent(ParsingSourceScopeWrapper owner)
			{
				this._owner = owner;

				++this._owner._indentLevel;
			}

			public void Dispose()
			{
				--this._owner._indentLevel;
			}
		}
	}
}
